/**
 * 函数：
 *      函数也是一个对象
 *      函数中可以封装一些功能（代码）,在需要时可以执行这些功能（代码）
 *      函数可以保存一些代码，在需要时调用
 *      使用typeof检查一个函数对象时，会返回function
 * */

/**
 * 创建一个函数对象
 *  可以将要封装的代码以字符串的形式传递给构造函数
 *  封装到函数中的代码不会立即执行，函数中的代码会在函数调用的时候执行
 *
 *  函数的调用：
 *      语法：函数对象();
 *  当调用函数时，函数中封装的代码会按照顺序进行执行
 * */
 var fun = new Function(" console.log('Hello JS Function...');");

 fun();

 /**===============以上这种方式在开发中基本上不用===============*/

 /**
  * 在开发中通常使用函数声明来创建一个函数：
  *     function 函数名([形参1， 形参2...]){语句...}
  * */

 function fun2() {
     console.log("Hello JS 函数声明...");
 }

 fun2();

 /**
  * 使用函数表达式来创建一个函数：
  *     var 函数名 = function([形参1， 形参2...]){语句...}
  * */

 var fun3 = function () {
     console.log("Hello JS 函数表达式...")
 };

 fun3();

 console.log("===========函数的参数==========");

 /**
  *
  * 可以在函数的()中来指定一个或者多个形参（形式参数）
  * 多个形参之间使用,隔开，声明形参就相当于在函数内部声明了对应的变量，但是并没有赋值
  *
  * 在调用函数时，可以在()中指定实参（实际参数），实参将会赋值给函数中对应的形参。
  * 调用函数时，解析器不会检查实参的类型，所以要注意是否有可能接收到非法的参数。
  *
  * 调用函数时，解析器也不会检查实参的数量,多余的实参不会被赋值
  * 如果实参的数量少于行参的数量，则没有对应形参的实参将是undefined
  * 函数的实参可以是任意的数据类型
  * */

 function fun4(a, b) {
     console.log(a + b);
 }

 fun4(3, true, 100);

 /**
  * 可以使用return来设置函数的返回值：
  *     语法：return 值
  *
  *     return后的值将会作为函数的执行结果返回，可以定义一个变量，来接收该结果。
  *     在函数中return后的语句都不会执行，
  *     return后面不添加任何内容，相当于  return undefined
  *     如果函数中不写return也会返回一个undefined
  *     return后面可以添加任意类型的值
  * */

 /**
  * 实参可以是任意的数据类型，也可以是一个对象
  *     当我们的参数过多时，可以将参数封装到一个对象中，然后通过对象传递。
  *
  * 实参既然可以是一个对象，（函数也是一种对象）那么函数也可以作为一个参数进行传递
  * */

 console.log("============函数作为传递参数============");

 var person = {name:'孙悟空', age:18, address:'花果山'};

 function sayHello(p) {
     console.log("姓名： " + p.name + ", 年龄： " + p.age + ", 住址： " + p.address);
 }

 function per(pern) {
     pern(person);
 }

 /**一般函数传递*/
 per(sayHello);

 /**函数中传递匿名函数*/
 per(function () {
     console.log("匿名函数传递......")
 });

 /**
  * 在上面实参传递函数时：
  *     sayHello
  *         函数对象，相当于直接使用函数对象
  *     sayHello()
  *         调用函数，相当于使用函数的返回值
  * */


 /**
  * 返回值的类型：
  *     使用return可以结束整个函数
  *     使用break可以退出当前循环
  *     使用continue可以退出当次循环
  *
  * 返回值可以是任意的数据类型，也可以是一个对象，同时也可以是一个函数
  *
  * */

 function funReturn() {
     console.log("函数要执行了...");
     for(var i = 0; i < 5; i++) {
         if(i == 2) {
             // break;
             // continue;
             return;
         }
         console.log(i);
     }
     console.log("函数执行完成了...");
 }

funReturn();


 /**返回值是对象*/
 function funObject() {
     var obj = {name: "沙和尚"};
     return obj;
 }
var abc = funObject();
 console.log(abc.name);
 
 /**将函数作为返回值*/
 
 function objFun() {
     function fun44() {
         console.log("我是函数中的函数...")
     }
     return fun44;
 }
 var ss = objFun();
 console.log(ss);

 /**
  * 立即执行函数
  *     函数定义完，立即被调用，这种函数叫做立即执行函数，立即执行函数往往只会执行一次
  *
  *     function () {}   --->  匿名函数体，直接写在程序中会报错，可以添加一个()
  *     遵循函数调用的方法，函数()的形式对函数进行调用。
  * */

 (function () {
     console.log("你是我的小苹果......")
 })();

(function (a, b) {
    console.log("a = " + a);
    console.log("b = " + b);
})(123, 456);

/**
 *
 * 对象的属相值可以是任意的数据类型，也可以是函数
 *
 * 函数也可以称为对象的属性，如果一个函数作为一个对象的属性保存，那么我们称这个函数是这个对象的方法，调用函数就说调用对象的方法。
 * 但是它只是名称上的区别，没有其他的区分
 * */

console.log("-------------------对象的属性值也可是是一个函数-------------------");
var persons = {

    name: "易阳宇"
};

persons.sayTo = function () {
    console.log(persons.name);
};

console.log(persons.sayTo);

persons.sayTo();

/**
 * 枚举对象中的属性：
 *      通俗的解释就是我需要取出该对象中的所有属性（在开发中往往不知道别人给到的对象中有哪些属性）
 *      语法：使用for ... in 语句
 *          for(var 变量 in 对象){}
 *      for...in语句，对象中有几个属性，循环体就会执行几次，每次执行，会将对象中的一个属性名字赋值给变量
 * */

var enumeration = {name: "孙悟空", age:18, address:"花果山"};

for(n in enumeration) {
    console.log(n);
    console.log(enumeration[n]);
}